package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
)

func main() {
	// 创建一个等待组，用于等待所有 Goroutines 完成
	var wg sync.WaitGroup

	// 创建一个通道，用于传递下载的网页内容
	pagesCh := make(chan string, 5)

	// 定义一组网页URL
	urls := []string{
		"https://www.example.com",
		"https://www.baidu.com",
		"https://www.github.com",
		"https://www.reddit.com",
		"https://www.stackoverflow.com",
	}

	// 启动多个 Goroutines 并发下载网页
	for _, url := range urls {
		wg.Add(1)
		go downloadPage(url, pagesCh, &wg)
	}

	// 启动一个 Goroutine 用于关闭通道并等待所有 Goroutines 完成
	go func() {
		wg.Wait()
		close(pagesCh)
	}()

	// 从通道中读取下载的网页内容
	for page := range pagesCh {
		fmt.Println("Downloaded:", len(page), "bytes")
	}
}

// downloadPage 用于下载网页并将内容发送到通道
func downloadPage(url string, ch chan<- string, wg *sync.WaitGroup) {
	// 在函数退出时调用 Done 减少等待组计数
	defer wg.Done()

	// 发起 HTTP 请求
	response, err := http.Get(url)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer response.Body.Close()

	// 读取网页内容
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Error reading response body:", err)
		return
	}

	// 将网页内容发送到通道
	ch <- string(body)
}

/**
Goroutines 是 Go 语言中一种轻量级线程的抽象，用于并发执行代码。Goroutines 是 Go 语言并发模型的基础组件之一，
它的设计目标是让并发编程变得更加简单和高效。
以下是 Goroutines 的主要特点和作用：

轻量级:

Goroutines 是轻量级的线程，可以在程序中创建成千上万个而不会导致性能下降。相较于传统的线程，Goroutines 的创建和切换开销更小。
并发执行:

Goroutines 可以在同一个程序中并发执行，允许多个任务同时进行，而不需要显式地管理线程。这使得编写并发代码更加直观和容易。
独立调度:

Go 语言运行时系统（runtime）负责 Goroutines 的调度和管理。Goroutines 的调度是由 Go 运行时自动完成的，开发者无需手动管理线程的创建、销毁和切换。
低成本的并发:

创建一个 Goroutine 的成本很低，因此可以轻松地启动大量的 Goroutines 来处理并发任务，而不会消耗过多的系统资源。
通信通过通道:

Goroutines 之间通过通道（Channel）进行通信，而不是通过共享内存。这种通信方式避免了传统并发编程中可能出现的共享数据问题，使得编写安全的并发程序更加容易。
避免显式的锁:

Go 语言推崇通过通信来共享内存，而不是通过共享内存来通信。这意味着在 Goroutines 之间共享数据时，不需要显式地使用锁，减少了死锁和竞态条件的发生概率。
易于理解和维护:

使用 Goroutines 和通道的编程风格使得并发代码更加易于理解和维护。相较于传统的锁和线程的方式，Go 的并发模型提供了更高级别、更安全的抽象。
通过 Goroutines，Go 语言鼓励开发者编写高效且易于理解的并发程序，同时简化了并发编程的复杂性。这种并发模型是 Go 语言成功的一个关键因素，使得它在处理高并发、
大规模并行任务的场景中表现出色。
*/
